﻿using System;
using System.Collections.Generic;
using Honeycomb.Protocol;

namespace Honeycomb.Core.Client
{
    /// <summary>
    /// Responsible for task scheduling.
    /// </summary>
    public sealed class TaskScheduler : IDisposable
    {
        /// <summary>
        /// List of TaskExecutionThreads, one per CPU-core.
        /// </summary>
        readonly List<TaskExecutionThread> taskExecutionThreads = new List<TaskExecutionThread>();

        /// <summary>
        /// Queue of unprocessed tasks.
        /// </summary>
        readonly BlockingQueue<TaskBase> taskQueue;

        /// <summary>
        /// Queue of unsent tasks.
        /// </summary>
        readonly Queue<TaskExecutionResult> resultQueue = new Queue<TaskExecutionResult>();

        /// <summary>
        /// Number of tasks to buffer.
        /// </summary>
        private readonly int taskBufferLength;

        /// <summary>
        /// Constructor.
        /// </summary>
        public TaskScheduler(int taskBufferLength)
        {
            this.taskBufferLength = taskBufferLength;
            taskQueue = new BlockingQueue<TaskBase>(taskBufferLength);

            // Create a TaskExecutionThread for each core.
            for (int i = 0; i < Environment.ProcessorCount; i++)
                taskExecutionThreads.Add(new TaskExecutionThread(taskQueue, resultQueue));
        }

        /// <summary>
        /// Schedule a task.
        /// </summary>
        /// <param name="task">Task to schedule.</param>
        /// <returns>True, if the buffer is full.</returns>
        public bool Schedule(TaskBase task)
        {
            return taskQueue.Enqueue(task) >= taskBufferLength;
        }

        /// <summary>
        /// Retrieves one result from the result queue.
        /// </summary>
        /// <returns></returns>
        public TaskExecutionResult GetExecutionResult()
        {
            return resultQueue.Dequeue();
        }

        /// <summary>
        /// Dispose.
        /// </summary>
        public void Dispose()
        {
            foreach (TaskExecutionThread taskThread in taskExecutionThreads)
                taskThread.Dispose();
        }
    }
}